import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;

import bl.IntegersTokenClassesManager;


public class main {

	static SecureRandom sr = new SecureRandom();
	
	static void testBigIntegerAndBytesCastings() throws Exception
	{
		// choose random BigInteger
		SecureRandom sc = new SecureRandom();
		int counter = 0;
		for (int i = 0; i < 1000; i++)
		{
			BigInteger x = new BigInteger(104, sc);

			byte[] bytes = Common.CreatePaddedBigIntegerBytes(x, 128); 
			if (bytes.length > 128 && bytes[0]!=0)
				counter++;
			BigInteger y = new BigInteger(bytes);
			
			if (y.compareTo(x)!=0)
				throw new Exception("X !+ Y");
		}
		System.out.println(counter);
	}
	
	static boolean TestIntegersTokens() throws Exception
	{
		int epoch = sr.nextInt(18376498);
		
		// via the this interface you create instances of the relevant methods.
		TokenClassesManagerI manager = new IntegersTokenClassesManager(); // SPECIFIES THE IMPLEMENTATION OF THE VEB TOKENS (notice that from here, you won't see the word Integers)
		
		// contains the public parameters
		PublicParametersI ppPO = manager.CreatePublicParameters(1024);
		
		// The PO knows only the public parameters
		VEBTokensGeneratorI POsideTokenGenerator = manager.CreateVEBTokenGenerator(ppPO);
				
		/*
		 * The scenario:
		 * 0. The PO generates bytes that represent the public parameters, and the clients pull them and create a PublicParameterI object.
		 * 1. The friend pushes message to the client. In this test, only the bytes of the token are being sent.
		 * 2. The PO receives pull request from the client. The client sends the token, and a NIZK proof. (all are sent in bytes) 
		 * 3. The test verify that both the friend and the client created the same VEB token.
		 * 4. The PO verify the proof against the token.
		 */
		
		// 0. The client and her friend pull the public parameters as byte[] representation and generate the public-parameters as an object.
		byte[] ppBytes = ppPO.GetBytes();

		// verify the length of the public parameters bytes representation
		if (ppBytes.length != ppPO.GetPublicParametersLengthBytes())
			throw new Exception("The byte[] representation of the public parameters is different from the data in the public information");

		PublicParametersI ppClient = manager.CreatePublicParameters(ppBytes);
		PublicParametersI ppFriend = manager.CreatePublicParameters(ppBytes);
		
		
		// The client knows the public parameters and also his own secret key and the details of its friends.
		// In this example, the client knows the "public" part of the identity (like in the simple scenario).
		VEBTokensGeneratorI ClientSideTokenGenerator = manager.CreateVEBTokenGenerator(ppClient);
		
		// Generate Secret Key (for the PRF, and possibly for messages encryption), and create identity for the client (both secret and "public" parameters)
		FriendClientDataI clientDetails = manager.GenerateNewClient(ppClient);

		// Create the data of the client that is sent to its friends, by sharing the key and the public identity:
		ClientDataI publicClientData = manager.CreateClientDataI(clientDetails.getIdentityPublicParameter());
		FriendClientDataI clientAsFriend = manager.CreateFriendClientDataI(publicClientData, clientDetails.getCommonSecretKey());
		
		// Create the friend's token generator
		VEBTokensGeneratorI FriendSideTokenGenerator = manager.CreateVEBTokenGenerator(ppFriend);

		
		// 1. The friend pushes message to the client using the 
		VEBTokenI friendToClientToken = FriendSideTokenGenerator.CreateToken(epoch, clientAsFriend);
		byte[] friendToClientTokenBytes = friendToClientToken.getBytes(ppFriend);
		
		// Assume that the PO received the bytes of the token with a message
		
		// 2. The client sends pull request to pull the message with a proof
		// first create the token
		VEBTokenI clientPullToken = ClientSideTokenGenerator.CreateToken(epoch, clientDetails);
		byte[] clientPullTokenBytes = clientPullToken.getBytes(ppClient);
		// then create the proof
		NIZKproofI clientProof = ClientSideTokenGenerator.CreateProof(clientPullToken, clientDetails.getClientData());
		byte[] clientProofBytes = clientProof.GetBytes();
		
		// 3. Verify that the tokens that were sent by both the client and her friend are identical
		// bytes comparison:
		if (!Arrays.equals(friendToClientTokenBytes, clientPullTokenBytes))
			throw new Exception("Sender and recipient tokens are not identical in byte[] representation");

		// verify the length of the token bytes representation
		if (friendToClientTokenBytes.length != ppFriend.GetTokenLengthBytes())
			throw new Exception("The byte[] representation of the token is different from the data in the public information");

		// verify the length of the proof bytes representation
		if (clientProofBytes.length != ppClient.GetProofLengthBytes())
			throw new Exception("The byte[] representation of the proof is different from the data in the public information");
			
		// tokens reproduced by the PO comparison		
		VEBTokenI senderToken = POsideTokenGenerator.ParseTokenBytes(friendToClientTokenBytes);
		VEBTokenI recipientToken = POsideTokenGenerator.ParseTokenBytes(clientPullTokenBytes);
		if (!senderToken.Compare(recipientToken))
			throw new Exception("Tokens used for push and pull are not identical");

		// 4. The PO verifies the proof against the token
		NIZKproofI proofReceivedByPO = POsideTokenGenerator.ParseProofBytes(clientProofBytes);
		if (!recipientToken.Verify(proofReceivedByPO, ppClient))
			throw new Exception("Verification of token faild");
		
		// ADD here comparison between two proofs, similarly to the comaprison between two VEBToken.
		
		return true;
	}

	
	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		int testsCounter = 5000;
		int successesCounter = 0;
		for (int i = 0; i < testsCounter; i++)
		{
			if (TestIntegersTokens())
			{
				successesCounter++;
			}
		}
		
		if (successesCounter == testsCounter)
		{
			System.out.println("Test succeeded");
		}
	}
}
